package com.googlecode.yoohoo.xmppcore.connection;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;

import com.googlecode.yoohoo.utils.AppUtils;
import com.googlecode.yoohoo.xmppcore.ConfigManager;
import com.googlecode.yoohoo.xmppcore.protocol.IError;
import com.googlecode.yoohoo.xmppcore.protocol.XmlLangText;
import com.googlecode.yoohoo.xmppcore.protocol.XmppProtocolException;
import com.googlecode.yoohoo.xmppcore.protocol.stanza.error.StanzaError;
import com.googlecode.yoohoo.xmppcore.protocol.stream.error.InternalServerError;
import com.googlecode.yoohoo.xmppcore.stream.INegotiationContext;
import com.googlecode.yoohoo.xmppcore.stream.IStreamNegotiant;

public class DefaultConnectionHandler implements IConnectionHandler {
	private static final Log log = LogFactory.getLog(DefaultConnectionHandler.class);
	private boolean errorAttachSenderXml;;
	
	public DefaultConnectionHandler(BundleContext bundleContext) {
		errorAttachSenderXml = AppUtils.getConfigManager(bundleContext).getBoolean(
				ConfigManager.KEY_ERROR_ATTACH_SENDER_XML);
	}
	
	@Override
	public void messageReceived(IConnectionContext context, String message) {
		try {
			processMessage(context, (String)message);
		} catch (XmppProtocolException e) {
			IError error = ((XmppProtocolException)e).getError();
			context.write(error.toXml());

			if (error.shouldCloseStream()) {
				context.close();
			}
		}
	}

	private void processMessage(IConnectionContext context, String message) {
		if (context instanceof INegotiationContext) {
			INegotiationContext negotiationContext = (INegotiationContext)context;
			IStreamNegotiant negotiant = negotiationContext.getNegotiant();
			
			if (negotiant == null) {
				if (log.isWarnEnabled()) {
					log.warn("Can't get stream negotiant.");
					
					context.close();
					return;
				}
			}
			
			try {
				negotiant.negotiate(negotiationContext, message);
			} catch (Exception e) {
				handleStreamError(e, message);
			}
		} else {
			// TODO
			try {
				//processStanza(context, message);
			} catch (Exception e) {
				handleStanzaError(e, message);
			}
		}
	}
	
	
	/*private void updateLastActivatedTime(ISession session) {
	((Session)session).setLastActivatedTime(System.currentTimeMillis());
}*/
	
	private void handleStreamError(Exception e, String senderXml) {
		if (e instanceof XmppProtocolException && ((XmppProtocolException)e).getError() != null) {
			handleXmppError((XmppProtocolException)e, e, senderXml);
		} else {
			handleXmppError(new XmppProtocolException(new InternalServerError()), e, senderXml);
		}
	}
	
	private void handleStanzaError(Exception e, String senderXml) {
		if (e instanceof XmppProtocolException && ((XmppProtocolException)e).getError() != null) {
			handleXmppError((XmppProtocolException)e, e, senderXml);
		} else {
			handleXmppError(new XmppProtocolException(
				new com.googlecode.yoohoo.xmppcore.protocol.stanza.error.InternalServerError()),
					e, senderXml);
		}
	}
	
	private void handleXmppError(XmppProtocolException protocolException, Exception cause, String senderXml) {
		IError error = protocolException.getError();
		if (error.getText() == null && cause != null && cause.getMessage() != null) {
			error.setText(new XmlLangText(cause.getMessage()));
		}
	
		if ((error instanceof StanzaError)) {
			StanzaError stanzaError = (StanzaError)error;
			stanzaError.setSenderXml(senderXml);
		
			if (errorAttachSenderXml)
				stanzaError.setAttachSenderXml(true);
		}
	
		throw protocolException;
	}
	
	@Override
	public void messageSent(IConnectionContext context, String message) {
		// TODO Auto-generated method stub

	}

	@Override
	public void sessionClosed(IConnectionContext context) {
		// TODO Auto-generated method stub

	}

	@Override
	public void sessionIdle(IConnectionContext context) {
		// TODO Auto-generated method stub
		/*		if (log.isTraceEnabled())
		log.trace(String.format("Session %s is idle", session.toString()));
	
	IXmppContext context;
	if (session.containsAttribute(AppConstants.KEY_SESSION)) {
		context = (IXmppContext)session.getAttribute(AppConstants.KEY_SESSION);
	} else if (session.containsAttribute(AppConstants.KEY_NEGOTIATION_CONTEXT)) {
		context = (IXmppContext)session.getAttribute(AppConstants.KEY_NEGOTIATION_CONTEXT);
	} else {
		return;
	}
	
	for (Object key : context.getAttributeKeys()) {
		if (key instanceof ConnectionTimeoutMonitorKey) {
			ConnectionTimeoutMonitor value = (ConnectionTimeoutMonitor)context.getAttribute(key);
			
			if (value.isTimeout()) {
				String errorText = null;
				if (value.shouldCloseStream()) {
					errorText = String.format("Processing for IQ '%s' is timeout. Server closes the stream.",
						((ConnectionTimeoutMonitorKey)key).getId());
				} else {
					errorText = String.format("Processing for IQ '%s' is timeout.", ((ConnectionTimeoutMonitorKey)key).getId());
				}
				
				ConnectionTimeout error = new ConnectionTimeout();
				error.setText(new XmlLangText(errorText));
				
				session.write(error.toXml());
				if (value.shouldCloseStream()) {
					closeSession(session);
				} else {
					context.removeAttribute(key);
				}
				
				if (log.isDebugEnabled()) {
					log.debug(errorText);
				}
				
				return;
			}
		}
	}*/
	}
	
	/*private void closeSession(IConnectionContext context) {
		context.close(true);
	}*/

	@Override
	public void sessionOpened(IConnectionContext context) {
		// TODO Auto-generated method stub

	}

}
